/*************************************/
/* monitor.c                         */
/* Implement the state machine to    */
/* communicate with the TS-7300      */
/*************************************/

#include "monitor.h"

/* Scheduler includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

//MESSAGES
#define ALIVE_MSG       0x61 //ASCII a
#define SILENCE_MSG     0x65 //ASCII e
#define ALIVE_ACK_MSG   0x72 //ASCII r
#define SILENCE_ACK_MSG 0x6F //ASCII o
#define START_MSG       0x73 //ASCII s
#define START_ACK_MSG   0x74 //ASCII t
//portTICK_RATE_MS constant is used to convert milliseconds into ticks.
#define DELAY_10_MS   (10 / portTICK_RATE_MS)
//maximal time that the supervisor will wait for the first alive signal to be received
//#define TS7300_INIT_TIME    12000   //this is 2min wait time because this task executes every 10ms
#define TS7300_INIT_TIME    300   //FOR DEBUG
//maximal time that the supervisor will wait for the alive signal to be received
#define ALIVE_TIME    300   //this is 3s wait time because this task executes every 10ms
#define RESET_TIME    3000  //time that the supervisor will wait on the alarm state
//before activating a reset on the TS-7300
typedef enum {ALARM=0, INIT, ALIVE_RCV, WTG_FOR_ALIVE, SILENCE, SILENCE_ACK,
              REINIT} State_type;

//private methods (states)
void Alarm();
void Init();
void AliveRcv();
void WtgForAlive();
void Silence();
void SilenceAck();
void Reinit();
void InitializeMonitorSM();

/* This table contains a pointer to the function to call in each state.*/
void(*state_table[])()={Alarm, Init, AliveRcv, WtgForAlive, Silence,
                        SilenceAck, Reinit};

extern xQueueHandle xUartSendQueue;
extern xQueueHandle xUartReceiveQueue;
State_type curr_state;
portSHORT cntTSinitTime;
portSHORT cntAliveSignal;
portSHORT cntStartAckSignal;
portSHORT cntResetTime;

void vMonitorTask( void *pvParameters ){
  portTickType xLastWakeTime;
  
  /* The xLastWakeTime variable needs to be initialized with the current tick
  count. Note that this is the only time the variable is written to explicitly.
  After this xLastWakeTime is updated automatically internally within
  vTaskDelayUntil(). */
  xLastWakeTime = xTaskGetTickCount();
  InitializeMonitorSM();
  
  for (;;){
    //call the function corresponding to the current state
    state_table[curr_state]();
    /* This task should execute exactly every 10 milliseconds */
    vTaskDelayUntil( &xLastWakeTime, DELAY_10_MS );    
  }
}

void InitializeMonitorSM(){
    curr_state = INIT;
    cntTSinitTime = 0;
    cntAliveSignal = 0;
    cntStartAckSignal = 0;
    cntResetTime = 0;
}

/*wait for a maximum of TS7300_INIT_TIME until TS-7300 initialize
  we should receive the ALIVE message if not trigger the alarm*/
void Init(){
    portCHAR cMessageReceived;
    if(cntTSinitTime > TS7300_INIT_TIME){
        curr_state = ALARM;
        cntTSinitTime = 0;
    } else {
        if( xQueueReceive(xUartReceiveQueue, &cMessageReceived, 0) == pdPASS ){
            if( cMessageReceived == ALIVE_MSG ){
                curr_state = ALIVE_RCV;
                cntTSinitTime = 0;
            } else if( cMessageReceived == SILENCE_MSG ){
                curr_state = SILENCE;
                cntTSinitTime = 0;
            }
        }
        cntTSinitTime++;    
    }
}

/*this state means that we received the alive signal so we have
  to send an acknowledge to the TS-7300*/
void AliveRcv(){
    portCHAR aliveAck = ALIVE_ACK_MSG;
    /* Send the message to the queue, waiting for 10 ticks for space become available
    should the queue already be full. */
    if( xQueueSendToBack( xUartSendQueue, &aliveAck, 10 ) == pdPASS ){
        curr_state = WTG_FOR_ALIVE;
    }
}

/*this state means that we are awaiting the alive signal to be received
  if we receive the alive signal then we go back to ALIVE_RCV state.
  if we have not received the alive signal after ALIVE_TIME/2 we send the 
  ALIVE_ACK again, if after ALIVE_TIME we dont receive the alive signal then 
  we activate the alarm*/
void WtgForAlive(){
    portCHAR cMessageReceived;
    portCHAR aliveAck = ALIVE_MSG;
    if( xQueueReceive(xUartReceiveQueue, &cMessageReceived, 0) == pdPASS ){
        if( cMessageReceived == ALIVE_MSG ){
            curr_state = ALIVE_RCV;
            cntAliveSignal = 0;
        } else if( cMessageReceived == SILENCE_MSG ){
            curr_state = SILENCE;
            cntAliveSignal = 0;
        }
    }
    if(cntAliveSignal > ALIVE_TIME){
        curr_state = ALARM;
        cntAliveSignal = 0;
    } else if( cntAliveSignal == (ALIVE_TIME/2) ){
        //we send again the alive signal
        xQueueSendToBack( xUartSendQueue, &aliveAck, portMAX_DELAY );
    }
    cntAliveSignal++;
}

/*We activate the Alarm, only if we received a ALIVE_MSG we go out
 of the Alarm state or if we received a SILENCE_MSG
 After RESET_TIME we activate a reset on the TS-7300 and go to the
 init state.*/
void Alarm(){
    portCHAR cMessageReceived;
    P5OUT |= 0x02; //activate Alarm
    if( xQueueReceive(xUartReceiveQueue, &cMessageReceived, 0) == pdPASS ){
        if( cMessageReceived == ALIVE_MSG ){
            curr_state = ALIVE_RCV;
            P5OUT &= 0xFD; //turn off Alarm
        } else if( cMessageReceived == SILENCE_MSG ){
            curr_state = SILENCE;
            P5OUT &= 0xFD; //turn off Alarm
        }
    }
    cntResetTime++;
    if(cntResetTime > RESET_TIME){    
      P4OUT &= 0xFE; //activate reset
      vTaskDelay(10/portTICK_RATE_MS); //sleep 10ms
      P4OUT |= 0x01; 
      cntResetTime = 0;
      curr_state = INIT;
    }
}

/*In Silence state it means the TS-7300 send a silence_msg and we 
should deactivate the Alarm and all the protocol of monitoring but before
we should send a Silence_ack_msg*/
void Silence(){
    portCHAR silenceAck = SILENCE_ACK_MSG;
    /* Send the message to the queue, waiting for 10 ticks for space become available
    should the queue already be full. */
    if( xQueueSendToBack( xUartSendQueue, &silenceAck, 10 ) == pdPASS ){
        curr_state = SILENCE_ACK;
    }
}

/*We sent the silence_ack_msg so we can turn off the alarm and all
  communications until we receive a start_msg*/
void SilenceAck(){
    portCHAR cMessageReceived;
    portCHAR startAck = START_ACK_MSG;
    P5OUT &= 0xFD; //turn off Alarm
    if( xQueueReceive(xUartReceiveQueue, &cMessageReceived, 0) == pdPASS ){
        if( cMessageReceived == START_MSG ){
            //send the start acknowledge
            xQueueSendToBack( xUartSendQueue, &startAck, portMAX_DELAY );
            curr_state = REINIT;
        }
    }
}

/*We just came from the silence state so we wait again for an alive signal
if we receive it we go to the state Waiting_for_alive and go back to normal*/
void Reinit(){
    portCHAR cMessageReceived;
    portCHAR startAck = START_ACK_MSG;
    if( xQueueReceive(xUartReceiveQueue, &cMessageReceived, 0) == pdPASS ){
        if( cMessageReceived == ALIVE_MSG ){
            curr_state = ALIVE_RCV;
            cntStartAckSignal = 0;
        } else if( cMessageReceived == SILENCE_MSG ){
            curr_state = SILENCE;
            cntStartAckSignal = 0;
        }
    }
    if(cntStartAckSignal > ALIVE_TIME){
        curr_state = ALARM;
        cntStartAckSignal = 0;
    } else if( cntStartAckSignal == (ALIVE_TIME/2) ){
        //we send again the alive signal
        xQueueSendToBack( xUartSendQueue, &startAck, portMAX_DELAY );
    }
    cntStartAckSignal++;
}